using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace MageDefender.Graphics
{
    /// <summary>
    /// This class is used to store and display static models.
    /// </summary>
    public class DBModel : Microsoft.Xna.Framework.DrawableGameComponent
    {
        int id;

        Effect effect;
        Model m_Model;
        Matrix renderMatrix, worldMatrix, viewMatrix, projMatrix;
        Vector3 vecEye;
        Matrix[] bones;

        Texture2D texColor, texNormal, texIcon;

        Vector3 ambientLightColor;
        Vector3 diffuseLightColor;
        Vector3 specularLightColor;
        float specularPower;

        string name = "";
        string nameTexNormal = "";
        string nameTexColor = "";
        string nameIcon;
        string nameShader = "Shaders\\Shader";
        string techniqueShader = "NormalMapping";

        bool initialized = false;
        float rotZ = 0.0f;
        float rotY = 0.0f;
        float rotX = 0.0f;
        float transZ = 0.0f;
        float transY = 0.0f;
        float transX = 0.0f;

        public float RotX
        {
            get { return rotX; }
            set { rotX = value; }
        }

        public float RotY
        {
            get { return rotY; }
            set { rotY = value; }
        }

        public float RotZ
        {
            get { return rotZ; }
            set { rotZ = value; }
        }

        public float TransX
        {
            get { return transX; }
            set { transX = value; }
        }

        public float TransY
        {
            get { return transY; }
            set { transY = value; }
        }

        public float TransZ
        {
            get { return transZ; }
            set { transZ = value; }
        }

        public bool Initialized
        {
            get { return initialized; }
            set { initialized = value; }
        }

        #region Properties
        public Texture2D TexIcon
        {
            get { return texIcon; }
            set { texIcon = value; }
        }

        public string NameIcon
        {
            get { return nameIcon; }
            set { nameIcon = value; }
        }

        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }

        public Vector3 AmbientLightColor
        {
            get { return ambientLightColor; }
            set { ambientLightColor = value; }
        }

        public Vector3 DiffuseLightColor
        {
            get { return diffuseLightColor; }
            set { diffuseLightColor = value; }
        }

        public Vector3 SpecularLightColor
        {
            get { return specularLightColor; }
            set { specularLightColor = value; }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public string TextureNormal
        {
            get
            {
                return nameTexNormal;
            }
            set
            {
                nameTexNormal = value;
            }
        }

        public string TextureColor
        {
            get
            {
                return nameTexColor;
            }
            set
            {
                nameTexColor = value;
            }
        }

        public string ShaderName
        {
            get
            {
                return nameShader;
            }
            set
            {
                nameShader = value;
            }
        }

        public string ShaderTechnique
        {
            get
            {
                return techniqueShader;
            }
            set
            {
                techniqueShader = value;
            }
        }
        #endregion


        public DBModel(Game game)
            : base(game)
        {
            m_Model = null;
            AmbientLightColor = new Vector3(0.5f, 0.5f, 0.5f);
            DiffuseLightColor = new Vector3(0.8f, 0.8f, 0.8f);
            SpecularLightColor = new Vector3(0.0f, 0.0f, 0.0f);
            SpecularPower = 32.0f;

            Id = -1;

            nameIcon = "";

            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            effect = Game.Content.Load<Effect>("Shaders\\Shader");

            int width = Game.GraphicsDevice.Viewport.Width;
            int height = Game.GraphicsDevice.Viewport.Height;
            
            worldMatrix = Matrix.Identity;
            Game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(Game.GraphicsDevice, VertexPositionNormalTexture.VertexElements);

            effect.CurrentTechnique = effect.Techniques["ToonShader"];

            initialized = true;

            base.Initialize();
        }

        public void Initialize(Matrix proj, Matrix view)
        {
            projMatrix = proj;
            viewMatrix = view;

            this.Initialize();
        }

        protected override void LoadContent()
        {
            //effect = Game.Content.Load<Effect>(nameShader);

            m_Model = Game.Content.Load<Model>(name);
            bones = new Matrix[this.m_Model.Bones.Count];
            this.m_Model.CopyAbsoluteBoneTransformsTo(bones);

            texColor = Game.Content.Load<Texture2D>(nameTexColor);
            texNormal = Game.Content.Load<Texture2D>(nameTexNormal);

            if (nameIcon.Length > 0)
            {
                texIcon = Game.Content.Load<Texture2D>(nameIcon);
            }

            base.LoadContent();
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);
        }

        public void Update(Vector3 eye, Matrix view, Matrix render, GameTime gameTime)
        {
            viewMatrix = view;
            renderMatrix = render;
            vecEye = eye;

            this.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            #region NotInUse

            foreach (ModelMesh mesh in m_Model.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    // calculate our worldMatrix..
                    worldMatrix = bones[mesh.ParentBone.Index] * renderMatrix;
                }
            }

            Matrix worldInverse = Matrix.Invert(worldMatrix);
            Vector4 vLightDirection = new Vector4(0.0f, 0.0f, -1000.0f, 1.0f);
            effect.Parameters["matWorldViewProj"].SetValue(worldMatrix * viewMatrix * projMatrix);
            effect.Parameters["matInverseWorld"].SetValue(worldInverse);
            effect.Parameters["vLightDirection"].SetValue(vLightDirection);
            effect.Parameters["ColorMap"].SetValue(texColor);
            effect.Parameters["CelMap"].SetValue(texNormal);

            // Begin our effect
            effect.Begin(SaveStateMode.SaveState);

            // A shader can have multiple passes, be sure to loop trough each of them.
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                // Begin current pass
                pass.Begin();

                foreach (ModelMesh mesh in m_Model.Meshes)
                {
                    foreach (ModelMeshPart part in mesh.MeshParts)
                    {
                        // Render our meshpart
                        Game.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
                        Game.GraphicsDevice.Indices = mesh.IndexBuffer;
                        Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                                      part.BaseVertex, 0, part.NumVertices,
                                                                      part.StartIndex, part.PrimitiveCount);
                    }
                }

                // Stop current pass
                pass.End();
            }
            // Stop using this effect
            effect.End();
#endregion

            //Draw the model. A model can have multiple meshes, so loop.  
            //foreach (ModelMesh mesh in m_Model.Meshes)
            //{
            //    // This is where the mesh orientation is set, as well as our camera and projection.  
            //    foreach (BasicEffect effect2 in mesh.Effects)
            //    {
            //        effect2.EnableDefaultLighting();
            //        effect2.PreferPerPixelLighting = true;

            //        effect2.AmbientLightColor = AmbientLightColor;
            //        effect2.DiffuseColor = DiffuseLightColor;
            //        effect2.SpecularColor = SpecularLightColor;
            //        effect2.SpecularPower = SpecularPower;
            //        effect2.Texture = texColor;
            //        effect2.World = bones[mesh.ParentBone.Index] * renderMatrix;
            //        effect2.View = viewMatrix;
            //        effect2.Projection = projMatrix;
            //    }
            //    mesh.Draw();
            //}
            base.Draw(gameTime);
        }
    }
}